#include "hash.h"

unsigned int primeNumberSizes[] = {997, 1997, 4007, 8009, 16001, 32003};

// Using java hash function for Strings.
unsigned int hash(char *val, unsigned int size) {
   	int i;
    unsigned int hash = 0;
  
	for(; *val; val++) {	
		hash = MULT * hash + *val;
    }
    return (hash % size);
}

void initTable(table *t) {
    t->size_index = 0;
    t->size = primeNumberSizes[t->size_index];
	t->collisions = 0;

    // Calloc start everything with zero and NULL equals 0.
    t->rows = (row**) calloc(t->size, sizeof(row*));
	int i;
	for(i=0; i < t->size; i++) {
		t->rows[i] = NULL;
	}	
}

void destroyTable(table *t) {
    int i;
    // For all entries.
    for(i = 0; i < t->size; i++) {
        row *head = t->rows[i];
        // Free all the content.
		while(head != NULL) {
			row *aux = head;
			head = head->next;
			free(aux->value);
			free(aux);	
		}	
	}
	free(t->rows);
}

row* get(char *val, table *t) {
    unsigned int key = hash(val, t->size);
	row *r = t->rows[key];
	while((r != NULL) && (strcmp(r->value,val) != 0)) {
        r = r->next;
    }
    return r;
}

void rehashTable(table *t) {
	/* Initialize a new table */
	table newTable;
	newTable.size_index = t->size_index + 1;
	newTable.size = primeNumberSizes[newTable.size_index];
	newTable.collisions = 0;
	newTable.rows = (row**) calloc(newTable.size, sizeof(row*));
	int i;
	for(i=0; i < t->size; i++) {
		newTable.rows[i] = NULL;
	}

	//Move rows from old table to new table and delete old table
	for(i=0; i<t->size; i++) {
		row *r = t->rows[i];
		while(r != NULL) {
			put(r->value, r->type, &newTable);
			row *aux = r;
			r = r->next;
			free(aux->value);
			free(aux);	
		}	
	}
	free(t->rows);

	t->size_index = newTable.size_index;;
    t->size = primeNumberSizes[t->size_index];
	t->collisions = newTable.collisions;
	t->rows = newTable.rows;
}

row* put(char *val, unsigned int type, table *t) {
	// Initialize new row.	
	row *r = (row *) calloc(1,sizeof(row));
	r->scope = -1;  // Not yet filled.
	r->localI = -1; // Not yet filled.
    r->type = type;
    r->value = (char *) calloc(strlen(val)+1,sizeof(char));
    strcpy(r->value,val);
	
	// Find it's place
    unsigned int key = hash(val, t->size);
    
	/* Manage collisions */
	if((t->rows[key]) != NULL) {
		t->collisions++;
		// Resize, if too crowded.
		if(t->collisions > t->size){
            rehashTable(t);
			key = hash(val, t->size);
        }
	}
	
	// Add row to it's right place.
	row *pt_row = t->rows[key];
    row *pt_prev = NULL;
    while(pt_row != NULL){
    	pt_prev = pt_row;
    	pt_row  = pt_row->next; 
    }
	if(pt_prev == NULL){
		t->rows[key] = r;
	} else {
		pt_prev->next = r;
	}
	return r;
}

row* makeTemp(table *t) {
	static int nextLabel = 0;
	char buffer[128];
	
	sprintf(buffer, "__temp%d", ++nextLabel);
	put(buffer, SYMBOL_IDENTIFIER, t);
}

row* makeLabel(table *t) {
	static int nextLabel = 0;
	char buffer[128];
	
	sprintf(buffer, "__label%d", ++nextLabel);
	put(buffer, SYMBOL_IDENTIFIER, t);
}
